package br.org.bertol.mestrado.engine.optimisation.pso;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import br.org.bertol.mestrado.engine.exception.HeuristicsException;
import br.org.bertol.mestrado.engine.fitness.Objective;
import br.org.bertol.mestrado.engine.optimisation.AbstractMetaHeuristica;
import br.org.bertol.mestrado.engine.optimisation.entity.Particle;
import br.org.bertol.mestrado.engine.optimisation.moo.AbstractParetoDominace;

/**
 * @author contaqualquer
 */
public class ParticleSwarmOptimization<T> extends
        AbstractMetaHeuristica<T, Particle<T>> {

    /***/
    private final transient TypeMovement     tipoMovimento;

    /***/
    private final transient PsoRepository<T> psoRepository;

    /**
     * @param swarm
     *            Objeto
     * @param typeMovimento
     *            Objeto
     * @param numIteracoes
     *            Objeto
     * @param depth
     *            Objeto
     * @param repositorySize
     *            Objeto
     */
    public ParticleSwarmOptimization(final List<Particle<T>> swarm,
        final TypeMovement typeMovimento,
        final long numIteracoes, final int depth, final int repositorySize,
        final ArrayList<T> classes, final Objective[] objectives) {

        super(numIteracoes, classes, objectives);

        this.listaPareto.addAll(swarm);

        this.tipoMovimento = typeMovimento;

        psoRepository = new PsoRepository<T>(depth,
                repositorySize, objectives);

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void evaluate() throws HeuristicsException {

        try {
            initSwarm();

            for (int kIteration = 0; kIteration < numIteracoes; kIteration++) {
                evaluateSwarmParticle(kIteration);
            }
        } catch (Exception e) {
            throw new HeuristicsException(e);
        }
    }

    /**
     * Rezalia uma iteração do enxame.
     * @param kIteration
     *            iteração atual
     * @throws Exception
     *             Erro
     */
    @SuppressWarnings("unchecked")
    private void evaluateSwarmParticle(final int kIteration) throws Exception {

        Particle<T> gBest = null;

        // recupera melhor global
        if (objectives.length == 1) {
            gBest = psoRepository.getGbest();
        }

        for (final ListIterator<Particle<T>> iterator = listaPareto
                .listIterator(); iterator
                .hasNext();) {

            final Particle<T> particle = iterator.next();

            if (objectives.length > 1) {
                gBest = psoRepository.getGbest();
            }
            // atualiza gbest
            particle.updateGbest(gBest);

            // atualiza velocidade
            particle.updateVelocity(kIteration);

            // virificação in�cio: clona
            final Particle<T> dummy = (Particle<T>) particle.clone();

            // atualiza posição do clone
            tipoMovimento.execute(dummy);

            // caso movimenot v�lido
            // executa
            if (dummy.calcFitness()) {

                particle.calcFitness();

                final Particle<T> newParticle = particle.optimal(particle);

                if (paretoMinMinDominace.compare(newParticle.getHeight(),
                                                 newParticle
                        .getHpbest()) != AbstractParetoDominace.RIGHT_DOMINE) {
                    newParticle.updatePBest();
                }

                iterator.set(newParticle);

            }
        }

        // adiciona a part�cula no reposit�rio
        psoRepository.addInto(listaPareto);
    }

    /**
     * Inicia o Swarm.
     * @throws Exception
     *             Erro
     */
    private void initSwarm() throws Exception {

        for (final ListIterator<Particle<T>> iterator = listaPareto
                .listIterator(); iterator
                .hasNext();) {

            final Particle<T> particle = iterator.next();

            final Particle<T> newParticle = particle.optimal(particle);

            // atualiza posi��o
            tipoMovimento.execute(newParticle);

            newParticle.calcFitness();

            newParticle.updatePBest();

            iterator.set(newParticle);
        }

        psoRepository.startParetoBorder(listaPareto);

    }

    /**
     * Retorna todo o bando.
     * @return {@link List}
     */
    public final List<Particle<T>> getSwarm() {
        return listaPareto;
    }

    /**
     * @see PsoRepository#getParetoBorder()
     * @return Lista de soluções não dominadas
     */
    public final List<Particle<T>> getParetoBorder() {
        return psoRepository.getParetoBorder();
    }
}